package com.example.comsys.service.impl;

import com.example.comsys.dao.WageDAO;
import com.example.comsys.dataobject.WageDO;
import com.example.comsys.model.Attend;
import com.example.comsys.model.Employee;
import com.example.comsys.model.EmployeeBase;
import com.example.comsys.model.Page;
import com.example.comsys.model.Result;
import com.example.comsys.model.Wage;
import com.example.comsys.param.AttendQueryParam;
import com.example.comsys.param.PageQueryParam;
import com.example.comsys.param.WageQueryParam;
import com.example.comsys.service.AttendService;
import com.example.comsys.service.DepartmentService;
import com.example.comsys.service.EmployeeService;
import com.example.comsys.service.WageService;
import com.example.comsys.util.Constant;
import com.example.comsys.util.ConvertUtils.WageUtils;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFCellStyle;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.VerticalAlignment;
import org.apache.poi.ss.util.CellRangeAddress;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

/**
 * wage service impl
 *
 * @author wjh
 * @date 2020/1/4, 周六
 */
@SuppressWarnings("DuplicatedCode")
@Service
public class WageServiceImpl implements WageService {

  @Autowired
  private WageDAO wageDAO;

  @Autowired
  private AttendService attendService;

  @Autowired
  private DepartmentService departmentService;

  @Autowired
  private EmployeeService employeeService;

  @Override
  public Wage addWage(Wage wage) {
    return null;
  }

  @Override
  public List<Wage> queryAllWage(WageQueryParam param) {

    List<WageDO> wageDos = wageDAO.selectByParam(param);
    if (CollectionUtils.isEmpty(wageDos)) {
      return new ArrayList<>();
    }
    return wageDos.stream().map(WageUtils::wageToModel).filter(Objects::nonNull)
        .peek(v -> v.setRealGet(
            v.getBaseSalary() + v.getBaseBonus() + v.getBenefitBonus()
                - Math.abs(v.getDeBonus()) - Math.abs(v.getDeSalary()))
        )
        .collect(Collectors.toList());
  }

  @Override
  public Integer queryCount(WageQueryParam param) {
    param.setLimit(false);
    List<WageDO> wageDos = wageDAO.selectByParam(param);
    if (wageDos != null) {
      return wageDos.size();
    }
    return -1;
  }

  @Override
  public boolean updateWageByWageId(Wage wage) {
    WageDO wageDO = WageUtils.wageToDO(wage);
    int i = wageDAO.updateByPrimaryKey(wageDO);
    return (i == 1);
  }

  @Override
  public boolean deleteWage(long wageId) {
    return wageDAO.deleteByPrimaryKey(wageId) == 1;
  }

  @Override
  public Result<String> rebuildWageInfo(int sy, int sm, int ey, int em, long departId) {

    Result<String> result = new Result<>();
    result.setSuccess(false);

    // 如果是同一年的数据
    if (sy == ey) {
      // 删除已有的数据
      int delC = wageDAO.deleteWageInfo(sy, sm, ey, em, departId);

      AttendQueryParam attendQueryParam = new AttendQueryParam();
      attendQueryParam.setDepartId(departId);

      // 查询该部门的所有员工的考勤记录
      List<Attend> attendRecords = attendService.queryAttendsByParam(attendQueryParam);

      if (attendRecords == null || attendRecords.size() == 0) {
        result.setMessage("不存在该时间范围内的考勤记录");
        return result;
      }

      Map<Integer, Map<Integer, Map<Long, AttendMonthSummary>>> summary = getAttendMonthSummary(attendRecords);

      // 查询该部门的所有员工信息
      List<Employee> employees = employeeService.queryEmployeeByDepartment(departId);
      if (employees == null || employees.size() == 0) {
        result.setMessage("不存在该部门的员工信息");
        return result;
      }
      Map<Long, Employee> employeeMap = employees.stream()
          .collect(Collectors.toMap(EmployeeBase::getEmployeeId, e -> e));

      List<Wage> wages = new ArrayList<>();

      // 选定年份该部门员工的考勤记录
      Map<Integer, Map<Long, AttendMonthSummary>> yearMap = summary.get(sy);
      if (yearMap == null || yearMap.size() == 0) {
        result.setMessage("不存在该时间范围内的考勤记录");
        return result;
      }
      for (int month = sm; month <= em; month++) {
        Map<Long, AttendMonthSummary> monthSummary = yearMap.get(month);
        int finalMonth = month;
        if (monthSummary == null || monthSummary.size() == 0) {
          // 不跳过没有考勤记录的月份，强制生成按照基本工资和基本奖金的工资记录
          List<Wage> temp = employees.stream().map(employee -> {
            Wage wage = new Wage(employee.getBaseSalary(), employee.getBaseBonus(), 0, sy, finalMonth);
            wage.setEmployeeId(employee.getEmployeeId());
            wage.setName(employee.getName());
            return wage;
          }).collect(Collectors.toList());
          wages.addAll(temp);
          continue;
        }
        List<AttendMonthSummary> list = new ArrayList<>(monthSummary.values());
        List<Wage> wageList = list.stream().map(attendMonthSummary -> {
          Employee employee = employeeMap.get(attendMonthSummary.getEmployeeId());
          // 组装工资信息
          Wage wage = new Wage(employee.getBaseSalary(), employee.getBaseBonus(), attendMonthSummary.getDeSalary(), sy,
              finalMonth);
          wage.setEmployeeId(employee.getEmployeeId());
          wage.setName(employee.getName());
          return wage;
        }).collect(Collectors.toList());

        wages.addAll(wageList);
      }
      // 组装完成所有的记录
      if (wages.size() > 0) {
        List<WageDO> list = wages.stream().map(WageUtils::wageToDO).collect(Collectors.toList());
        if (list.size() > 0) {
          int i = wageDAO.batchInsert(list);
          result.setSuccess(true);
          result.setMessage(String.valueOf(delC));
          result.setCode(String.valueOf(i));
          return result;
        } else {
          result.setMessage("模型转换出错");
        }
      } else {
        result.setMessage("没有需要插入的工资信息(没有相应时间的考勤记录)");
      }
    } else {
      result.setMessage("请选择同一年的月份范围");
    }
    return result;
  }

  @Override
  public Page<Wage> queryDataByParam(WageQueryParam queryParam) {
    if (queryParam == null) {
      queryParam = new WageQueryParam();
    }
    PageQueryParam pageQueryParam;
    if (queryParam.getPageQueryParam() == null) {
      pageQueryParam = new PageQueryParam();
      queryParam.setPageQueryParam(pageQueryParam);
    } else {
      pageQueryParam = queryParam.getPageQueryParam();
    }

    queryParam.setDetailDepart(true);
    List<Wage> wages = queryAllWage(queryParam);

    Page<Wage> wagePage = new Page<>();

    wagePage.setCurrentPage(pageQueryParam.getCurrentPage());
    wagePage.setPageSize(pageQueryParam.getPageSize());

    if (wages != null && wages.size() > 0) {
      Integer totalSize = queryCount(queryParam);
      wagePage.setTotalSize(totalSize);
      wagePage.setTotalItems(wages.size());
      wagePage.setTotalPages(totalSize / queryParam.getPageQueryParam().getPageSize() + 1);
      wagePage.setData(wages);

    } else {
      wagePage.setTotalSize(0);
      wagePage.setTotalItems(0);
      wagePage.setData(null);
      wagePage.setTotalPages(0);
    }
    return wagePage;
  }

  /**
   * 根据attend列表生成汇总记录
   *
   * @param attendList 列表
   * @return <年份, <月份, 月份汇总记录>>
   */
  private static Map<Integer, Map<Integer, Map<Long, AttendMonthSummary>>> getAttendMonthSummary(
      List<Attend> attendList) {

    Map<Integer, Map<Integer, Map<Long, AttendMonthSummary>>> map = new HashMap<>(attendList.size() << 1);

    Map<Long, List<Attend>> collect = attendList.stream().collect(Collectors.groupingBy(Attend::getEmployeeId));
    collect.forEach((employeeId, attendList1) -> {

      if (attendList1 == null || attendList1.size() == 0) {
        // 没有此员工的考勤记录， 按没有任何违规记录处理
        // AttendMonthSummary summary = new AttendMonthSummary();
        // summary.setEmployeeId(employeeId);
        // summary.setYear();
        return;
      }
      attendList1.forEach(attend -> {

        LocalDate dateTime = attend.getDate();
        int year = dateTime.getYear();
        int month = dateTime.getMonthValue();

        Map<Integer, Map<Long, AttendMonthSummary>> yearSummaryMap = map.getOrDefault(year, new HashMap<>(12));

        Map<Long, AttendMonthSummary> monthSummaryMap = yearSummaryMap.getOrDefault(month, new HashMap<>());

        // 获取该员工的年-月的已有记录
        AttendMonthSummary monthSummary = monthSummaryMap.getOrDefault(employeeId, new AttendMonthSummary());

        int late = attend.getLate();
        int early = attend.getLeaveEarly();
        if (late > 0) {
          if (late <= 30) {
            monthSummary.setLateIn030(monthSummary.getLateIn030() + 1);
          } else {
            monthSummary.setLateG30(monthSummary.getLateG30() + 1);
          }
        }
        if (early > 0) {
          if (early <= 30) {
            monthSummary.setEarlyIn030(monthSummary.getEarlyIn030() + 1);
          } else {
            monthSummary.setEarlyG30(monthSummary.getEarlyG30() + 1);
          }
        }
        if (attend.isIllAbsence()) {
          monthSummary.setAbsenceCount(monthSummary.getAbsenceCount() + 1);
        }
        if (attend.isThingAbsence()) {
          monthSummary.setAbsenceCount(monthSummary.getAbsenceCount() + 1);
        }
        monthSummary.setEmployeeId(employeeId);
        monthSummaryMap.put(employeeId, monthSummary);
        yearSummaryMap.putIfAbsent(month, monthSummaryMap);
        map.putIfAbsent(year, yearSummaryMap);
      });
    });

    return map;
  }

  @Override
  public HSSFWorkbook generateExcel(String filename, Object... args) {
    WageQueryParam wageQueryParam = new WageQueryParam();
    wageQueryParam.setLimit(false);
    if (args != null) {
      Object arg1 = args[0];
      if (arg1 != null) {
        int year = (int) arg1;
        if (year > 0) {
          wageQueryParam.setYear(year);
        }
      }
      Object arg2 = args[1];
      if (arg2 != null) {
        int month = (int) arg1;
        if (month > 0 && month <= 12) {
          wageQueryParam.setMonth(month);
        }
      }
    }
    wageQueryParam.setLimit(false);
    wageQueryParam.setDetailDepart(true);
    List<Wage> wages = queryAllWage(wageQueryParam);
    if (wages == null || wages.size() == 0) {
      return null;
    }

    // 按照时间排个序
    List<Wage> collect = wages.stream().sorted((o1, o2) -> {
      int i = o2.getYear() - o1.getYear();
      if (i != 0) {
        return i;
      }
      return o2.getMonth() - o1.getMonth();
    }).collect(Collectors.toList());
    HSSFWorkbook wb = new HSSFWorkbook();
    //建立新的sheet对象（excel的表单）
    HSSFSheet sheet = wb.createSheet(filename);
    sheet.setVerticallyCenter(true);
    HSSFCellStyle styleMain = wb.createCellStyle();
    //水平居中
    styleMain.setAlignment(HorizontalAlignment.CENTER);
    //垂直居中
    styleMain.setVerticalAlignment(VerticalAlignment.CENTER);

    //在sheet里创建第一行，参数为行索引(excel的行)，可以是0～65535之间的任何一个
    HSSFRow row1 = sheet.createRow(0);
    //创建单元格（excel的单元格，参数为列索引，可以是0～255之间的任何一个
    HSSFCell cell = row1.createCell(0);
    row1.setHeight((short) 500);

    //设置单元格内容
    cell.setCellValue("各部门员工工资信息统计表" + (wageQueryParam.getMonth() == 0 ? "" : "【" + wageQueryParam.getMonth() + "】月"));
    //合并单元格CellRangeAddress构造参数依次表示起始行，截至行，起始列， 截至列
    sheet.addMergedRegion(new CellRangeAddress(0, 0, 0, 13));
    //在sheet里创建第二行

    HSSFRow row2 = sheet.createRow(1);
    //创建单元格并设置单元格内容
    row2.createCell(0).setCellValue("员工编号");
    row2.createCell(1).setCellValue("姓名");
    row2.createCell(2).setCellValue("基本薪资");
    row2.createCell(3).setCellValue("基本奖金");
    row2.createCell(4).setCellValue("绩效奖金");
    row2.createCell(5).setCellValue("扣除薪资");
    row2.createCell(6).setCellValue("扣除奖金");
    row2.createCell(7).setCellValue("年份");
    row2.createCell(8).setCellValue("月份");
    row2.createCell(9).setCellValue("上次修改时间");
    row2.createCell(10).setCellValue("备注");
    row2.setRowStyle(styleMain);
    //在sheet里创建第三行

    for (int i = 0; i < collect.size(); i++) {

      if (i <= 10) {
        sheet.setColumnWidth(i, 2588);
      } else {
        sheet.setColumnWidth(i, 6000);
      }

      Wage wage = collect.get(i);
      HSSFRow row = sheet.createRow(i + 2);
      row.setRowStyle(styleMain);

      row.createCell(0).setCellValue(wage.getEmployeeId());
      row.createCell(1).setCellValue(wage.getName());
      row.createCell(2).setCellValue(wage.getBaseSalary());
      row.createCell(3).setCellValue(wage.getBaseBonus());
      row.createCell(4).setCellValue(wage.getBenefitBonus());
      row.createCell(5).setCellValue(wage.getDeSalary());
      row.createCell(6).setCellValue(wage.getDeBonus());
      row.createCell(7).setCellValue(wage.getYear());
      row.createCell(8).setCellValue(wage.getMonth());
      row.createCell(9).setCellValue(wage.getGmtCreated().toString());
      row.createCell(10).setCellValue("无");
    }
    return wb;
  }
}

@Getter
@Setter
@ToString
class AttendMonthSummary {

  private long employeeId;

  /**
   * 迟到0-30分钟的次数
   */
  private int lateIn030;
  /**
   * 迟到大于30分钟的次数
   */
  private int lateG30;

  /**
   * 早退0-30分钟的次数
   */
  private int earlyIn030;

  /**
   * 早退大于30分钟的次数
   */
  private int earlyG30;

  /**
   * 请假（除去公休）次数
   */
  public int absenceCount;


  private int year;
  private int month;

  public int getDeSalary() {
    return lateIn030 * Constant.LATE_FOR_WORK_DE_SALARY_0_30
        + lateG30 * Constant.LATE_FOR_WORK_DE_SALARY_30_
        + earlyIn030 * Constant.EARLY_LEAVE_DE_SALARY_0_30
        + earlyG30 * Constant.EARLY_LEAVE_DE_SALARY_30_
        + absenceCount * Constant.ABSENCE_DAY_DE_SALARY;
  }


}
